﻿namespace Nomo.CoreModule
{
    /// <summary>
    /// 对象池。
    /// </summary>
    /// <typeparam name="T">对象池中成员的类型。</typeparam>
    [System.Serializable]
    public sealed class ObjectPool<T> : System.Collections.Generic.IEnumerable<T>
    {
        private readonly System.Collections.Generic.Stack<T> _stack;
        private readonly System.Func<T>                      _instanceCreator;
        private readonly System.Action<T>                    _onGet;
        private readonly System.Action<T>                    _onRelease;

        /// <summary>
        /// 通过定义如何创建一个新的成员、在获取成员时要执行的操作，以及在回收成员时要执行的操作，初始化 <see cref="ObjectPool{T}"/> 类的新实例。
        /// </summary>
        /// <param name="instanceCreator">当想要从 <see cref="ObjectPool{T}"/> 中获取成员，但 <see cref="ObjectPool{T}"/> 中没有成员时，如何创建出一个新的成员。</param>
        /// <param name="onGet">从 <see cref="ObjectPool{T}"/> 中取出成员时，要对取出的成员执行的操作。</param>
        /// <param name="onRelease">向 <see cref="ObjectPool{T}"/> 放回成员时，要对放回的成员执行的操作。</param>
        /// <exception cref="System.ArgumentNullException"><paramref name="instanceCreator"/> 为 <see langword="null"/> .</exception>
        public ObjectPool(System.Func<T> instanceCreator, System.Action<T> onGet, System.Action<T> onRelease)
        {
            if (instanceCreator == null)
            {
                throw new System.ArgumentNullException(nameof(instanceCreator));
            }

            _stack           = new System.Collections.Generic.Stack<T>();
            _instanceCreator = instanceCreator;
            _onGet           = onGet;
            _onRelease       = onRelease;
        }

        /// <summary>
        /// 从 <see cref="ObjectPool{T}"/> 中取出一个成员。
        /// </summary>
        /// <returns>取出的成员。</returns>
        public T Get()
        {
            var t = _stack.Count != 0 ? _stack.Pop() : _instanceCreator.Invoke();
            _onGet?.Invoke(t);
            return t;
        }

        /// <summary>
        /// 将指定的对象放回或添加到 <see cref="ObjectPool{T}"/> 中。
        /// </summary>
        /// <param name="element">要放回的成员。</param>
        public void Release(T element)
        {
            _onRelease?.Invoke(element);
            _stack.Push(element);
        }

        /// <summary>
        /// 将指定集合的成员放回或添加到 <see cref="ObjectPool{T}"/> 中。
        /// </summary>
        /// <param name="elements">集合。</param>
        public void Release(System.Collections.Generic.IEnumerable<T> elements)
        {
            foreach (var element in elements)
            {
                _onRelease?.Invoke(element);
                _stack.Push(element);
            }
        }

        /// <summary>
        /// 从 <see cref="ObjectPool{T}"/> 中移除所有成员。
        /// </summary>
        public void Clear()
        {
            _stack.Clear();
        }

        /// <summary>
        /// 获取可用于枚举对象池的成员的枚举器。
        /// </summary>
        /// <returns>可用于枚举对象池的成员的枚举器。</returns>
        public System.Collections.Generic.IEnumerator<T> GetEnumerator() => _stack.GetEnumerator();

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => _stack.GetEnumerator();

        System.Collections.Generic.IEnumerator<T> System.Collections.Generic.IEnumerable<T>.GetEnumerator() => _stack.GetEnumerator();
    }
}
